/*
	This file is part of Erosion, a clone of Qix
	Copyright (c) 2009 Bill Whitacre

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

#include "system.h"
#include "extensions.h"

#include <windows.h>
#include <GL/gl.h>
#include <GL/wglext.h>

#include <xinput.h>

#pragma comment(lib,"xinput.lib")

/* Data structure for holding the Win32 video context with GL. */
typedef struct GLWindow
{
	int _is_registered;
	int get_started;

    HWND hwnd;                                  /* Window object handle. */
    HDC hdc;                                    /* Device context handle. */
	HGLRC glrc;                                 /* WGL rendering context.*/

	unsigned int width,height;
    unsigned int depth,fs;

    rat_key_map *keymap;

    char name[2048];

    int resizedevent;
    int apptermevent;

    float mousex,mousey;
    char buttons[8];
    char keys[256];
} GLWindow;

typedef struct CONTROLLER_STATE
{
    XINPUT_STATE state;
	XINPUT_VIBRATION vibe;
    int is_connected;
} CONTROLLER_STATE;

#define RAT_WINDOW_CLASS "RAT_WINDOW_CLASS"
/* Flag stating whether or not our window class is registered. */

GLWindow GLWin; /* Pointer for the main context. */

CONTROLLER_STATE controllers[4];

LRESULT CALLBACK rat_hwnd_proc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp);

/* Create a keymap for win32 vkeys. */
static void gen_w32_keymap(rat_key_map *keymap)
{
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_LBUTTON,RAT_KEY_LBUTTON));
	rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RBUTTON,RAT_KEY_RBUTTON));
	rat_key_map_add_pair(keymap,rat_key_pair_create(VK_CANCEL,RAT_KEY_CANCEL)); // ???
	rat_key_map_add_pair(keymap,rat_key_pair_create(VK_MBUTTON,RAT_KEY_MBUTTON));
#ifdef _MSC_VER
	rat_key_map_add_pair(keymap,rat_key_pair_create(VK_XBUTTON1,RAT_KEY_XBUTTON1));
	rat_key_map_add_pair(keymap,rat_key_pair_create(VK_XBUTTON2,RAT_KEY_XBUTTON2));
#endif
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_BACK,RAT_KEY_BACK));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_TAB,RAT_KEY_TAB));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_CLEAR,RAT_KEY_CLEAR));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RETURN,RAT_KEY_RETURN));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SHIFT,RAT_KEY_SHIFT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_CONTROL,RAT_KEY_CONTROL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_MENU,RAT_KEY_MENU));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_PAUSE,RAT_KEY_PAUSE));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_CAPITAL,RAT_KEY_CAPITAL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_KANA,RAT_KEY_KANA));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_HANGEUL,RAT_KEY_HANGUEL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_HANGUL,RAT_KEY_HANGUL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_JUNJA,RAT_KEY_JUNJA));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_FINAL,RAT_KEY_FINAL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_HANJA,RAT_KEY_HANJA));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_KANJI,RAT_KEY_KANJI));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_ESCAPE,RAT_KEY_ESCAPE));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_CONVERT,RAT_KEY_CONVERT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NONCONVERT,RAT_KEY_NONCONVERT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_ACCEPT,RAT_KEY_ACCEPT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_MODECHANGE,RAT_KEY_MODECHANGE));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SPACE,RAT_KEY_SPACE));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_PRIOR,RAT_KEY_PRIOR));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NEXT,RAT_KEY_NEXT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_END,RAT_KEY_END));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_HOME,RAT_KEY_HOME));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_LEFT,RAT_KEY_LEFT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_UP,RAT_KEY_UP));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RIGHT,RAT_KEY_RIGHT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_DOWN,RAT_KEY_DOWN));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SELECT,RAT_KEY_SELECT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_PRINT,RAT_KEY_PRINT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_EXECUTE,RAT_KEY_EXECUT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SNAPSHOT,RAT_KEY_SNAPSHOT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_INSERT,RAT_KEY_INSERT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_DELETE,RAT_KEY_DELETE));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_HELP,RAT_KEY_HELP));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_LWIN,RAT_KEY_LSUPER));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RWIN,RAT_KEY_RSUPER));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_APPS,RAT_KEY_APPS));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SLEEP,RAT_KEY_SLEEP));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD0,RAT_KEY_NUMPAD0));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD1,RAT_KEY_NUMPAD1));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD2,RAT_KEY_NUMPAD2));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD3,RAT_KEY_NUMPAD3));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD4,RAT_KEY_NUMPAD4));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD5,RAT_KEY_NUMPAD5));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD6,RAT_KEY_NUMPAD6));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD7,RAT_KEY_NUMPAD7));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD8,RAT_KEY_NUMPAD8));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMPAD9,RAT_KEY_NUMPAD9));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_MULTIPLY,RAT_KEY_MULTIPLY));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_ADD,RAT_KEY_ADD));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SEPARATOR,RAT_KEY_SEPARATOR));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SUBTRACT,RAT_KEY_SUBTRACT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_DECIMAL,RAT_KEY_DECIMAL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_DIVIDE,RAT_KEY_DIVIDE));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F1,RAT_KEY_F1));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F2,RAT_KEY_F2));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F3,RAT_KEY_F3));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F4,RAT_KEY_F4));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F5,RAT_KEY_F5));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F6,RAT_KEY_F6));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F7,RAT_KEY_F7));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F8,RAT_KEY_F8));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F9,RAT_KEY_F9));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F10,RAT_KEY_F10));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F11,RAT_KEY_F11));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F12,RAT_KEY_F12));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F13,RAT_KEY_F13));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F14,RAT_KEY_F14));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F15,RAT_KEY_F15));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F16,RAT_KEY_F16));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F17,RAT_KEY_F17));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F18,RAT_KEY_F18));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F19,RAT_KEY_F19));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F20,RAT_KEY_F20));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F21,RAT_KEY_F21));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F22,RAT_KEY_F22));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F23,RAT_KEY_F23));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_F24,RAT_KEY_F24));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NUMLOCK,RAT_KEY_NUMLOCK));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_SCROLL,RAT_KEY_SCROLL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_LSHIFT,RAT_KEY_LSHIFT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RSHIFT,RAT_KEY_RSHIFT));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_LCONTROL,RAT_KEY_LCONTROL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RCONTROL,RAT_KEY_RCONTROL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_LMENU,RAT_KEY_LMENU));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_RMENU,RAT_KEY_RMENU));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_ATTN,RAT_KEY_ATTN));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_CRSEL,RAT_KEY_CRSEL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_EXSEL,RAT_KEY_EXSEL));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_EREOF,RAT_KEY_EREOF));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_PLAY,RAT_KEY_PLAY));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_ZOOM,RAT_KEY_ZOOM));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_NONAME,0));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_PA1,RAT_KEY_PA1));
    rat_key_map_add_pair(keymap,rat_key_pair_create(VK_OEM_CLEAR,RAT_KEY_OEM_CLEAR));
    rat_key_map_sort(keymap);
}

LRESULT CALLBACK rat_hwnd_proc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)
{
    int btnnum=-1;

    PAINTSTRUCT ps;
    rat_key_map *keymap;
    RAT_KEY_CODE ratkey;
    WPARAM vkey;

    /* Welcome to hell - Windows message proccessing. */
    if (!GLWin.get_started) return DefWindowProc(hwnd,msg,wp,lp);

	switch (msg) /* Handle messages. */
    {
    case WM_CREATE:
        ShowWindow(hwnd,SW_SHOW);
        return 0;
        break;
    case WM_DESTROY:
        /* Destroy context.  First release the device context. */
        ReleaseDC(hwnd,GLWin.hdc);
        if (GLWin.fs) /* If in fullscreen... */
        {
            /* Restore the device mode. */
            if (ChangeDisplaySettings(NULL,0)!=DISP_CHANGE_SUCCESSFUL)
            {
                printf("Device mode restore failed!  WIN32ERROR==0x%08x",GetLastError());
                return 0;
            }
            GLWin.fs=0; /* No longer in full screen. */
        }
        return 0;
    case WM_QUIT:
    case WM_CLOSE:
        GLWin.apptermevent=1; /* Quit/Close message received, set app terminate.  We're going down. */
        return 0;
    case WM_SIZE:
        GLWin.height=HIWORD(lp);
        GLWin.width=LOWORD(lp);
        GLWin.resizedevent=1;

        ReleaseDC(GLWin.hwnd,GLWin.hdc);
        GLWin.hdc=GetDC(GLWin.hwnd);

        return 0;
    case WM_ERASEBKGND: return 0;
    case WM_PAINT:
        /* No painting. */
        BeginPaint(hwnd,&ps);
        EndPaint(hwnd,&ps);

        return 0;
    case WM_ACTIVATEAPP:
        return 0;
    case WM_KEYDOWN:
        vkey=wp;
        ratkey=rat_key_map_get_code(GLWin.keymap,vkey);

        if (ratkey)
            GLWin.keys[ratkey]=1;
        else
            GLWin.keys[vkey]=1;

        return 0;
    case WM_KEYUP:
        vkey=wp;
        ratkey=rat_key_map_get_code(GLWin.keymap,vkey);

        if (ratkey)
            GLWin.keys[ratkey]=0;
        else
            GLWin.keys[vkey]=0;

        return 0;
    case WM_RBUTTONDOWN: btnnum++; /* Right mouse button pressed */
    case WM_MBUTTONDOWN: btnnum++; /* Middle mouse button pressed */
    case WM_LBUTTONDOWN: btnnum++; /* Left mouse button pressed */
        GLWin.buttons[btnnum]=1;
        return 0;
    case WM_RBUTTONUP: btnnum++; /* Right mouse button released */
    case WM_MBUTTONUP: btnnum++; /* Middle mouse button released */
    case WM_LBUTTONUP: btnnum++; /* Left mouse button released */
        GLWin.buttons[btnnum]=0;
        return 0;
    case WM_MOUSEMOVE:
        GLWin.mousex=(double)LOWORD(lp)/(double)GLWin.width;
        GLWin.mousey=(double)HIWORD(lp)/(double)GLWin.height;
        return 0;
    case WM_SYSCOMMAND:
        /* System call made. */
        if (wp==SC_SCREENSAVE||wp==SC_MONITORPOWER) return 0;
        break;
    default:
        break;
    }

    return DefWindowProc(hwnd,msg,wp,lp); /* Default window proc. */
}

static int get_pfd_object(int bpp,PIXELFORMATDESCRIPTOR *pfd)
{
	memset(pfd,0,sizeof(PIXELFORMATDESCRIPTOR));
    pfd->nSize=sizeof(PIXELFORMATDESCRIPTOR);
    pfd->nVersion=1;
    pfd->dwFlags=PFD_SUPPORT_OPENGL|PFD_DRAW_TO_WINDOW|PFD_DOUBLEBUFFER;
    switch (bpp)
    {
    case 32:
		pfd->iPixelType=PFD_TYPE_RGBA;
		pfd->cColorBits=32;
		pfd->cAlphaBits=32;
		pfd->cDepthBits=32;
		pfd->cStencilBits=1;
		break;
	case 16:
		pfd->iPixelType=PFD_TYPE_RGBA;
        pfd->cRedBits=5;
        pfd->cGreenBits=6;
        pfd->cBlueBits=5;
		pfd->cDepthBits=16;
		pfd->cStencilBits=1;
		break;
    default: /* No match. */
        return 0; /* Failiure. */
    }
    pfd->dwDamageMask=0;
    pfd->iLayerType=PFD_MAIN_PLANE;

    return 1; /* Success! */
}

int rat_start_system_core(const char *name,unsigned int vw,
	unsigned int vh,unsigned int vbpp,int vfs,int resizeable)
{
	WNDCLASSEX wc;
    DWORD dwExStyle;       // Window Extended Style
    DWORD dwStyle;         // Window Style
    RECT windowRect;
	PIXELFORMATDESCRIPTOR pfd;
	int pfmt;
	char path[4096];

	unsigned screenwidth=GetSystemMetrics(SM_CXSCREEN);
	unsigned screenheight=GetSystemMetrics(SM_CYSCREEN);

	memset(&GLWin,0,sizeof(GLWindow));
    strcpy(GLWin.name,name);

	if (GetModuleFileName(NULL,path,sizeof(path)))
	{
		char *slash=strrchr(path,'\\');
		if (slash) *slash='\0';
        _chdir(path);
	}

	printf("Registering application class...");

	wc.cbSize=sizeof(WNDCLASSEX);
	/* Video "style."  We take control of H/V redraw as well as claiming full ownership. */
	wc.style=CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
	wc.lpfnWndProc=rat_hwnd_proc;
	wc.cbClsExtra=0;
	wc.cbWndExtra=0;
	wc.hInstance=GetModuleHandle(NULL);
	wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
	wc.hCursor=LoadCursor(NULL,IDC_ARROW);
	wc.hbrBackground=NULL;
	wc.lpszMenuName=NULL;
	wc.lpszClassName=RAT_WINDOW_CLASS;
	wc.hIconSm=NULL;

	/* Register that puppy. */
	GLWin._is_registered=RegisterClassEx(&wc);
	if (!GLWin._is_registered)
	{
		printf("FAILED.\n");
		return 0;
	}
	else
		printf("DONE!\n");

	GLWin.keymap=rat_key_map_create();
    gen_w32_keymap(GLWin.keymap);

	if (vfs)                             // vfs?
    {
		DEVMODE dmScreenSettings;                   // device mode
		printf("Fullscreen requested.  Switching...");
        
        memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
        dmScreenSettings.dmSize = sizeof(dmScreenSettings);
        dmScreenSettings.dmPelsWidth = vw;         // screen width
        dmScreenSettings.dmPelsHeight = vh;           // screen height
        dmScreenSettings.dmBitsPerPel = vbpp;             // bits per pixel
        dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

        if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
        {
            // setting display mode failed, switch to windowed
			printf("FAILED.\n");
            vfs=0;
        }
		else
			printf("DONE!\n");
    }

    dwStyle=vfs?WS_POPUP:
		(WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|
		(resizeable?WS_SIZEBOX|WS_MAXIMIZEBOX:0));

	GLWin.fs=vfs;
	GLWin.width=vw;
	GLWin.height=vh;
	GLWin.depth=vbpp;

	windowRect.left=screenwidth/2-vw/2;
	windowRect.top=screenheight/2-vh/2;
	windowRect.right=screenwidth/2+vw/2;
	windowRect.bottom=screenheight/2+vh/2;

    AdjustWindowRect(&windowRect,dwStyle,FALSE);     // Adjust Window To True Requested Size

	printf("Creating window...");

    // class registered, so now create our window
    GLWin.hwnd = CreateWindowEx(0,                                 // extended style
        RAT_WINDOW_CLASS,                          // class name
        name,      // app name
        dwStyle|WS_CLIPCHILDREN|
        WS_CLIPSIBLINGS,
		vfs?0:windowRect.left,vfs?0:windowRect.top,     // x,y coordinate
        windowRect.right-windowRect.left,
        windowRect.bottom-windowRect.top, // width, height
        (void *)0,                               // handle to parent
        (void *)0,                               // handle to menu
        GetModuleHandle(NULL),           // application instance
        (void *)0);                              // no extra params

    // check if window creation failed (hwnd would equal NULL)
    if (!GLWin.hwnd)
	{
		printf("FAILED.\n");
		return 0;
	}
	printf("DONE!\n");

    ShowWindow(GLWin.hwnd,SW_SHOW);          // display the window
    UpdateWindow(GLWin.hwnd);                 // update the window

	GLWin.hdc=GetDC(GLWin.hwnd);
    if (!GLWin.hdc)
    {
        printf("Getting device context failed!  WIN32ERROR==0x%08x",GetLastError());
        return 0; /* Failiure. */
    }
    printf("Obtained device context.\n");

    if (!get_pfd_object(vbpp,&pfd)) return 0; /* Failiure. */

    /* Select a pixel format. */
    pfmt=ChoosePixelFormat(GLWin.hdc,&pfd);
    if (!pfmt)
    {
        printf("Selected pixel format failed!  WIN32ERROR==0x%08x",GetLastError());
        return 0; /* Failiure. */
    }
    printf("Selected pixel format.\n");

    /* Realize our selected pixel format. */
    if (!SetPixelFormat(GLWin.hdc,pfmt,&pfd))
    {
        printf("Pixel format could not be realized!  WIN32ERROR==0x%08x",GetLastError());
        return 0; /* Failiure. */
    }
    printf("Realized pixel format.\n");

    /* Create a WGL rendering context. */
    GLWin.glrc=wglCreateContext(GLWin.hdc);
    if (!GLWin.glrc)
    {
        printf("Failed to create WGL context!  WIN32ERROR==0x%08x",GetLastError());
        return 0; /* Failiure. */
    }
    printf("Created WGL context.\n");

    /* Make sure this context is our current rendering context. */
    if (!wglMakeCurrent(GLWin.hdc,GLWin.glrc))
    {
        printf("Failed to attach WGL context.  WIN32ERROR==0x%08x",GetLastError());
        return 0; /* Failiure. */
    }
    printf("Attached WGL context.\nCan't tell if there's direct rendering because Win32 does not\nhave a mechanism for handling that.\nThe window is good to go.\n");

	GLWin.get_started=1;

	XInputEnable(1);

	{
		register unsigned i;
		for (i=0; i<4; ++i)
		{
			memset(controllers+i,0,sizeof(CONTROLLER_STATE));
			controllers[i].is_connected=XInputGetState(i,&controllers[i].state)==ERROR_SUCCESS;
		}
	}

	return 1;
}

void rat_stop_system_core()
{
	XInputEnable(0);

	/* Release the current context. */
    wglMakeCurrent(GLWin.hdc,NULL);
    printf("Released rendering context.\n");

    /* Delete our context. */
    wglDeleteContext(GLWin.glrc);
    printf("Deleted rendering context.\n");

	DestroyWindow(GLWin.hwnd);
    printf("Destroyed window.\n");

    /* Free memory. */
    rat_key_map_destroy(GLWin.keymap);

	UnregisterClass(RAT_WINDOW_CLASS,GetModuleHandle(NULL));
}

void rat_video_size(unsigned int *vw,unsigned int *vh)
{
	*vw=GLWin.width;
	*vh=GLWin.height;
}

int rat_handle_events()
{
	register unsigned i;
	MSG msg;

    /* Go through all messages. */
	if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
	{
		TranslateMessage(&msg); /* Translate message in to usable form. */
		DispatchMessage(&msg);  /* Dispatch the message to our callback. */
	}

	for (i=0; i<4; ++i)
	{
		controllers[i].is_connected=XInputGetState(i,&controllers[i].state)==ERROR_SUCCESS;

		// zero value if thumbsticks are within the dead zone 
		if( (controllers[i].state.Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
			 controllers[i].state.Gamepad.sThumbLX >-XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ) &&
			(controllers[i].state.Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
			 controllers[i].state.Gamepad.sThumbLY >-XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ) )
		{
			controllers[i].state.Gamepad.sThumbLX = 0;
			controllers[i].state.Gamepad.sThumbLY = 0;
		}

		if( (controllers[i].state.Gamepad.sThumbRX < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
			 controllers[i].state.Gamepad.sThumbRX >-XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ) &&
			(controllers[i].state.Gamepad.sThumbRY < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
			 controllers[i].state.Gamepad.sThumbRY >-XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ) )
		{
			controllers[i].state.Gamepad.sThumbRX = 0;
			controllers[i].state.Gamepad.sThumbRY = 0;
		}

		XInputSetState(i,&controllers[i].vibe);
	}

	return 1; /* Success! */
}

PFNWGLSWAPINTERVALEXTPROC pwglSwapIntervalEXT=NULL;

int rat_update_video()
{
	// unclamp the swap interval
	if (!pwglSwapIntervalEXT) pwglSwapIntervalEXT=load_extension("wglSwapIntervalEXT");
	if (pwglSwapIntervalEXT)  pwglSwapIntervalEXT(0);

	/* Try swapping the buffers. */
    if (!SwapBuffers(GLWin.hdc))
    {
        /* If we failed it may because we lost the device context. */
        GLWin.hdc=GetDC(GLWin.hwnd);
        return SwapBuffers(GLWin.hdc); /* Tries to swap buffers and returns success. */
    }

	return 1;
}

void rat_get_mouse_position(float *x,float *y)
{
	*x=GLWin.mousex;
	*y=GLWin.mousey;
}

int rat_is_key_down(RAT_KEY_CODE key)
{
	return GLWin.keys[key];
}

int rat_is_mouse_down(int button)
{
	return GLWin.buttons[button];
}

int rat_appterm_event()
{
	return GLWin.apptermevent;
}

int rat_resized_event()
{
	return GLWin.resizedevent;
}

void rat_get_key_buffer(unsigned char *keys)
{
	memcpy(keys,GLWin.keys,256);
}

int rat_is_controller_connected(int controller)
{
	return controllers[controller].is_connected;
}

int rat_is_button_pressed(int controller,unsigned button)
{
	return controllers[controller].state.Gamepad.wButtons&button;
}

unsigned char rat_left_trigger(int controller)
{
	return controllers[controller].state.Gamepad.bLeftTrigger;
}

unsigned char rat_right_trigger(int controller)
{
	return controllers[controller].state.Gamepad.bRightTrigger;
}

void rat_left_thumbstick(int controller,short *x,short *y)
{
	*x=controllers[controller].state.Gamepad.sThumbLX;
	*y=controllers[controller].state.Gamepad.sThumbLY;
}

void rat_right_thumbstick(int controller,short *x,short *y)
{
	*x=controllers[controller].state.Gamepad.sThumbRX;
	*y=controllers[controller].state.Gamepad.sThumbRY;
}

void rat_set_rumble(int controller,unsigned short left,unsigned short right)
{
	controllers[controller].vibe.wLeftMotorSpeed=left;
	controllers[controller].vibe.wRightMotorSpeed=right;

	XInputSetState(controller,&controllers[controller].vibe);
}
